#pragma once 
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "../TcpServer.hpp"
class Util
{
public:
    //字符串分割函数(将src字符串按照sep字符串进行分割，得到的各个子串放到array中，返回子串的数量)
    static size_t Split(const std::string& src, const std::string& sep, std::vector<std::string>* arr)
    {
        size_t offset = 0;//偏移量
        while(offset < src.size())
        {
            size_t pos = src.find(sep, offset);//在src字符串偏移量offset处，开始向后查找sep字符/子串，返回查找的位置
            if (pos == std::string::npos)//没有找到特定字符,说明这已经是最后的子串了,整体放入vector
            {
                if (pos == src.size()) { break; }//最后一个字符刚好是sep，直接返回
                arr->push_back(src.substr(offset));//offset到末尾 
                return arr->size();
            }
            if (pos == offset)//说明找到的子串没有内容,跳过本轮循环 
            { 
                offset = pos + sep.size();//更新offset
                continue;
            }
            arr->push_back(src.substr(offset, pos-offset));
            offset = pos + sep.size();//更新offset
        }
        return arr->size();
    }
    //读取文件所有内容，将读取的内容放到Buffer中
    static bool ReadFile(const std::string& filename, std::string* buffer)
    {
        std::ifstream ifs(filename, std::ios::binary);
        if (ifs.is_open() == false) //打开文件失败
        { 
            ERROR_LOG("OPEN %s file failed!!", filename.c_str());
            return false; 
        }
        ifs.seekg(0, ifs.end);//文件指针移到末尾
        size_t fsize = ifs.tellg();//获取文件长度
        ifs.seekg(0, ifs.beg);//将文件指针移回开头
        buffer->resize(fsize);
        ifs.read(&(*buffer)[0], fsize);
        if (ifs.good() == false)//文件无法打开或者发生其他错误
        {
            ERROR_LOG("READ %s FILE FAILED!!", filename.c_str());
            ifs.close();
            return false;
        }
        ifs.close();
        return true;
    }
    //向文件写入数据
    static bool WriteFile(const std::string& filename, const std::string& buffer)
    {
        std::ofstream ofs(filename, std::ios::binary | std::ios::trunc);//二进制清空写入(trunc：存在则清空，不存在则先创建文件)
        if (ofs.is_open() == false)//打开失败
        {
            ERROR_LOG("OPEN %s file failed!!", filename.c_str());
            return false;
        }
        ofs.write(buffer.c_str(), buffer.size());
        if (ofs.good() == false)//文件无法打开或者发生其他错误
        {
            ERROR_LOG("Write %s FILE FAILED!!", filename.c_str());
            ofs.close();
            return false;
        }
        ofs.close();
        return true;
    }
    //URL编码,避免URL中的资源路径与查询字符串中的特殊字符与HTTP请求中特殊字符产生歧义
    //编码格式：将特殊字符的ascii值，转换为两个16进制字符。例如前缀% C++即为C%2B%2B +的ascii是43（即为16*2+11=0X2B=43）
    //RFC3986文档规定不编码的特殊字符  .-_~以及字母及数字
    //RFC3986文档规定，编码格式%HH（%加两个16进制数字的格式）
    //W3C标准规定，查询字符串的空格，需要编码为+，解码则是+转空格
    static std::string UrlEncode(const std::string& url, bool convertSpaceToPlus)//convertSpaceToPlus:空格是否需要转+
    {
        std::string res;
        for (auto& c : url)
        {
            if (c == '.' || c == '-' || c == '_' || c == '~' || isalnum(c))//RFC3986文档规定不编码的特殊字符  .-_~以及字母及数字
            {
                res += c;
                continue;
            }
            if (c == ' ' && convertSpaceToPlus == true)//空格如果需要转换为+
            {
                res += '+';
                continue;
            }
            //剩下的字符都是需要编码成%HH格式
            char tmp[4] = {0};
            snprintf(tmp, 4, "%%%02X", c);
            res += tmp;
        }
        return res;
    }
    static char HexToI(char c)
    {
        if (c >= 0 && c <= '9') { return c - '0'; }
        else if (c >= 'a' && c <= 'z') { return c - 'a' + 10; }
        else if (c >= 'A' && c <= 'Z') { return c - 'A' + 10; }
        return -1;
    }
    //URL解码
    static std::string UrlDecode(const std::string& url, bool convertPlusToSpace)
    {
        //遇到了%，则将紧随其后的2个字符，转换为数字（第一个数字左移4位（乘16），然后加上第二个数字）
        std::string res;
        for (int i = 0; i < url.size(); ++i)
        {
            if (url[i] == '+' && convertPlusToSpace == true)
            {
                res += ' ';
                continue;
            }
            if (url[i] == '%' && (i + 2 < url.size()))
            {
                char v1 = HexToI(url[i+1]);
                char v2 = HexToI(url[i+2]);
                char v = (v1 << 4) + v2;
                res += v;
                i += 2;
                continue;
            }
            res += url[i];
        }
        return res;
    }
    //响应状态码的描述信息获取
    static std::string StateDesc(int state)
    {
        std::unordered_map<int, std::string> stateMsg = {
            {100,  "Continue"},
            {101,  "Switching Protocol"},
            {102,  "Processing"},
            {103,  "Early Hints"},
            {200,  "OK"},
            {201,  "Created"},
            {202,  "Accepted"},
            {203,  "Non-Authoritative Information"},
            {204,  "No Content"},
            {205,  "Reset Content"},
            {206,  "Partial Content"},
            {207,  "Multi-Status"},
            {208,  "Already Reported"},
            {226,  "IM Used"},
            {300,  "Multiple Choice"},
            {301,  "Moved Permanently"},
            {302,  "Found"},
            {303,  "See Other"},
            {304,  "Not Modified"},
            {305,  "Use Proxy"},
            {306,  "unused"},
            {307,  "Temporary Redirect"},
            {308,  "Permanent Redirect"},
            {400,  "Bad Request"},
            {401,  "Unauthorized"},
            {402,  "Payment Required"},
            {403,  "Forbidden"},
            {404,  "Not Found"},
            {405,  "Method Not Allowed"},
            {406,  "Not Acceptable"},
            {407,  "Proxy Authentication Required"},
            {408,  "Request Timeout"},
            {409,  "Conflict"},
            {410,  "Gone"},
            {411,  "Length Required"},
            {412,  "Precondition Failed"},
            {413,  "Payload Too Large"},
            {414,  "URI Too Long"},
            {415,  "Unsupported Media Type"},
            {416,  "Range Not Satisfiable"},
            {417,  "Expectation Failed"},
            {418,  "I'm a teapot"},
            {421,  "Misdirected Request"},
            {422,  "Unprocessable Entity"},
            {423,  "Locked"},
            {424,  "Failed Dependency"},
            {425,  "Too Early"},
            {426,  "Upgrade Required"},
            {428,  "Precondition Required"},
            {429,  "Too Many Requests"},
            {431,  "Request Header Fields Too Large"},
            {451,  "Unavailable For Legal Reasons"},
            {501,  "Not Implemented"},
            {502,  "Bad Gateway"},
            {503,  "Service Unavailable"},
            {504,  "Gateway Timeout"},
            {505,  "HTTP Version Not Supported"},
            {506,  "Variant Also Negotiates"},
            {507,  "Insufficient Storage"},
            {508,  "Loop Detected"},
            {510,  "Not Extended"},
            {511,  "Network Authentication Required"}
        };
        auto it = stateMsg.find(state);
        if (it != stateMsg.end()) { return it->second; }//找到了
        return "Unknow";
    }
    //根据文件后缀名获取mime
    static std::string ExtMime(const std::string& filename)
    {
         std::unordered_map<std::string, std::string> mimeMsg = {
            {".aac",        "audio/aac"},
            {".abw",        "application/x-abiword"},
            {".arc",        "application/x-freearc"},
            {".avi",        "video/x-msvideo"},
            {".azw",        "application/vnd.amazon.ebook"},
            {".bin",        "application/octet-stream"},
            {".bmp",        "image/bmp"},
            {".bz",         "application/x-bzip"},
            {".bz2",        "application/x-bzip2"},
            {".csh",        "application/x-csh"},
            {".css",        "text/css"},
            {".csv",        "text/csv"},
            {".doc",        "application/msword"},
            {".docx",       "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
            {".eot",        "application/vnd.ms-fontobject"},
            {".epub",       "application/epub+zip"},
            {".gif",        "image/gif"},
            {".htm",        "text/html"},
            {".html",       "text/html"},
            {".ico",        "image/vnd.microsoft.icon"},
            {".ics",        "text/calendar"},
            {".jar",        "application/java-archive"},
            {".jpeg",       "image/jpeg"},
            {".jpg",        "image/jpeg"},
            {".js",         "text/javascript"},
            {".json",       "application/json"},
            {".jsonld",     "application/ld+json"},
            {".mid",        "audio/midi"},
            {".midi",       "audio/x-midi"},
            {".mjs",        "text/javascript"},
            {".mp3",        "audio/mpeg"},
            {".mpeg",       "video/mpeg"},
            {".mpkg",       "application/vnd.apple.installer+xml"},
            {".odp",        "application/vnd.oasis.opendocument.presentation"},
            {".ods",        "application/vnd.oasis.opendocument.spreadsheet"},
            {".odt",        "application/vnd.oasis.opendocument.text"},
            {".oga",        "audio/ogg"},
            {".ogv",        "video/ogg"},
            {".ogx",        "application/ogg"},
            {".otf",        "font/otf"},
            {".png",        "image/png"},
            {".pdf",        "application/pdf"},
            {".ppt",        "application/vnd.ms-powerpoint"},
            {".pptx",       "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
            {".rar",        "application/x-rar-compressed"},
            {".rtf",        "application/rtf"},
            {".sh",         "application/x-sh"},
            {".svg",        "image/svg+xml"},
            {".swf",        "application/x-shockwave-flash"},
            {".tar",        "application/x-tar"},
            {".tif",        "image/tiff"},
            {".tiff",       "image/tiff"},
            {".ttf",        "font/ttf"},
            {".txt",        "text/plain"},
            {".vsd",        "application/vnd.visio"},
            {".wav",        "audio/wav"},
            {".weba",       "audio/webm"},
            {".webm",       "video/webm"},
            {".webp",       "image/webp"},
            {".woff",       "font/woff"},
            {".woff2",      "font/woff2"},
            {".xhtml",      "application/xhtml+xml"},
            {".xls",        "application/vnd.ms-excel"},
            {".xlsx",       "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
            {".xml",        "application/xml"},
            {".xul",        "application/vnd.mozilla.xul+xml"},
            {".zip",        "application/zip"},
            {".3gp",        "video/3gpp"},
            {".3g2",        "video/3gpp2"},
            {".7z",         "application/x-7z-compressed"}
        };
        size_t pos = filename.find_last_of('.');//从后往前找.
        if (pos == std::string::npos) { return "application/octet-stream"; }//没找到，return文件类型是二进制文件
        std::string ext = filename.substr(pos);//扩展名
        auto it = mimeMsg.find(ext);
        if (it == mimeMsg.end()) { return "application/octet-stream"; }//没找到，return文件类型是二进制文件
        return it->second;
    }
    //判断一个文件是否是目录
    static bool IsDirectory(const std::string& filename)
    {
        struct stat st;
        int ret = stat(filename.c_str(), &st);
        if (ret < 0) { return false; }//获取失败
        return S_ISDIR(st.st_mode);//根据st.st_mode判断filename是否是目录
    }
    //判断一个文件是否是一个普通文件
    static bool IsRegular(const std::string& filename)
    {
        struct stat st;
        int ret = stat(filename.c_str(), &st);
        if (ret < 0) { return false; }//获取失败
        return S_ISREG(st.st_mode);//根据st.st_mode判断filename是否是目录
    }
    //http请求的资源路径有效性判断
    //   /index.html-----前边的/叫做相对根目录，映射的是某个服务器上的子目录
    //意思就是，客户端只能请求相对根目录中的资源，其他地方的资源不予理会
    //但是客户端可能会出现/../login的请求，这个路径中的..会让路径的查找跑到相对根目录之外，这是不安全不合理的
    static bool validPath(const std::string& path)
    {
        //按照/进行路径分割，根据有多少子目录，计算目录深度有多少层
        int level = 0;//层次
        std::vector<std::string> subdir;
        Split(path, "/", &subdir);
        for (auto& dir : subdir)
        {
            if (dir == "..") 
            { 
                --level; 
                if (level < 0) { return false; }//层次有问题
            }
            else { ++level; }
        }
        return true;
    }
};